package com.eric.demo.snowflake;

import io.netty.util.internal.ConcurrentSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * @author guangyin.zhang
 * @since 2019/6/11
 */
public class IdUtil {

    private static Logger logger = LoggerFactory.getLogger(IdUtil.class);

    private static IdGenerator idGenerator ;

    private static long workerId = 1;

    private static long datacenterId =1;

    private static ZkClient zkClient = ZkClient.getInstance();

    private static final String ID_CONFIG_PATH = "/YMM56/ID/";

    public static void main(String[] args){
        int threads = 1000;
        int size = 10;
        CountDownLatch countDownLatchStart = new CountDownLatch(threads);
        CountDownLatch countDownLatch = new CountDownLatch(threads);
        Set<Long> s = new ConcurrentSet<>();
        for(int i = 0; i< threads; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        countDownLatchStart.await();
                        for(int j = 0; j< size; j++){
                            Long id = Long.valueOf(IdUtil.getId("order-center-service"));
                            s.add(id);
                            /*if(!s.contains(id)){
                                s.add(id);
                            }else{
                                //System.out.println(Thread.currentThread().getId() + ":" + System.currentTimeMillis() + ":" + id + ":重复");
                            }*/
                        }
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                    countDownLatch.countDown();
                }
            }).start();
            countDownLatchStart.countDown();
        }
        try {
            countDownLatch.await();
            System.out.println(s.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param appName 应用名
     * @throws Exception
     */
    public static synchronized long getId(String appName) throws Exception{
        if(idGenerator == null) {
            workerId = getWorkerId(appName);
            idGenerator = new SnowflakeIdGenerator(workerId,datacenterId);
        }
        return idGenerator.nextId();
    }

    private static long getWorkerId(String appName) throws Exception {
        String ip = NetUtils.getFirstLocalIp();
        String path = ID_CONFIG_PATH + appName;
        Map<String, String> workerIdMap = new HashMap<>();
        if (zkClient.exists(path)) {
            List<String> childPath = zkClient.getChildren(path);
            if (!CollectionUtils.isEmpty(childPath) && childPath.contains(ip)) {
                String workerId = zkClient.get(path + "/" + ip);
                return Integer.parseInt(workerId);
            }
            if (!CollectionUtils.isEmpty(childPath)) {
                for (int i = 0; i < childPath.size(); i++) {
                    String workerId = zkClient.get(path + "/" + childPath.get(i));
                    workerIdMap.put(workerId, childPath.get(i));
                }
            }
        }

        // workerId取机器ip转long后mod 31
        workerId = IPv4.longValue(ip) % 31;

        // workerId是否已被占用，如被占用直接+1循环遍历取下一个直到取到未被占用的workerId
        int i = 0;
        do {

            String existIp = workerIdMap.get(workerId + "");
            if (StringUtils.isEmpty(existIp)) {
                zkClient.createOrSet(path + "/" + ip, workerId + "");
                return workerId;
            }

            workerId = workerId + 1;
            workerId = (31 - workerId) >= 0 ? workerId : Math.abs(32 - workerId);
            ++i;
        } while (i <= 31);

        logger.error("workerId已经达到最大值");

        return workerId;
    }
}
